﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Security.Cryptography; 

using DupeKill.Core.IO; 

namespace DupeKill.Core.Comparison
{
    internal class ByContentFileSetComparer : IFileSetComparer 
    {
        private IFileSystemGateway fileSystem;
        private int chunkSize;

        public ByContentFileSetComparer(IFileSystemGateway fileSystem, int chunkSize)
        {
            if (fileSystem == null)
                throw new ArgumentNullException();
            if (chunkSize < 1)
                throw new ArgumentException();
            this.chunkSize = chunkSize; 
            this.fileSystem = fileSystem; 
        }

        #region IFileSetComparer Member

        public IEnumerable<FileSet> CompareAndSplit(FileSet fileSet)
        {
            int fileSetCount = 1;
            int[,] fileSets = new int[fileSet.Count, fileSet.Count];

            for (int i = 0; i < fileSet.Count; i++)
            {
                fileSets[0, i] = i; 
            }

            using (FileSetReader reader = new FileSetReader(fileSystem, fileSet, chunkSize))
            {
                while (reader.Read())
                {
                    int newSetCounter = 0; 

                    for (int s = 0; s < fileSetCount; s++)
                    {
                        for (int i = 1; i < fileSet.Count; i++)
                        {
                            int head = fileSets[s, 0];
                            int curr = fileSets[s, i];

                            if (curr > -1)
                            {
                                if (!reader.CompareFiles(head, curr))
                                {
                                    bool match = false; 
                                    fileSets[s, i] = -1;

                                    for (int k = 0; k < newSetCounter; k++)
                                    {
                                        int indexOfNewSet = fileSetCount + k;

                                        if (reader.CompareFiles(fileSets[indexOfNewSet, 0], curr))
                                        {
                                            InsertIntoSet(fileSets, indexOfNewSet, curr);
                                            match = true;
                                            break; 
                                        }
                                    }

                                    if (!match)
                                    {
                                        InsertIntoNewSet(fileSets, fileSetCount + newSetCounter, curr);
                                        newSetCounter++;
                                    }
                                }
                            }
                        }
                    }
                    fileSetCount += newSetCounter; 
                }
            }

            return CreateFileSets(fileSet, fileSets, fileSetCount); 
        }

        #endregion             

        private void InsertIntoSet(int[,] fileSets, int indexOfNewSet, int curr)
        {
            for (int i = 0; i < fileSets.GetLength(1); i++)
            {
                if (fileSets[indexOfNewSet, i] == -1)
                {
                    fileSets[indexOfNewSet, i] = curr;
                    return; 
                }

            }
        }

        private void InsertIntoNewSet(int[,] fileSets, int indexOfNewSet, int curr)
        {
            fileSets[indexOfNewSet, 0] = curr;
            for (int l = 1; l < fileSets.GetLength(1); l++)
            {
                fileSets[indexOfNewSet, l] = -1;
            }
        }

        private IEnumerable<FileSet> CreateFileSets(FileSet fileSet, int[,] fileSets, int fileSetCount)
        {
            for (int i = 0; i < fileSetCount; i++)
            {
                FileSet resultCandidate = null;

                for (int j = 0; j < fileSet.Count; j++)
                {
                    
                    if (fileSets[i, j] > -1)
                    {
                        if (resultCandidate == null)
                            resultCandidate = new FileSet();

                        resultCandidate.Add(fileSet[fileSets[i, j]]);
                    }
                }
 
                if (resultCandidate != null)
                    yield return resultCandidate;
            }
        }
    }
}
